package com.qiangesoft.bootcodegen.service.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.bootcodegen.constant.CodegenConstant;
import com.qiangesoft.bootcodegen.constant.DbType;
import com.qiangesoft.bootcodegen.entity.BcgDataSource;
import com.qiangesoft.bootcodegen.mapper.BcgDataSourceMapper;
import com.qiangesoft.bootcodegen.mapper.DbMapper;
import com.qiangesoft.bootcodegen.pojo.bo.BcgTableBO;
import com.qiangesoft.bootcodegen.pojo.bo.BcgTableColumnBO;
import com.qiangesoft.bootcodegen.pojo.dto.BcgDataSourceDTO;
import com.qiangesoft.bootcodegen.pojo.query.BcgDataSourceQuery;
import com.qiangesoft.bootcodegen.pojo.query.PageQuery;
import com.qiangesoft.bootcodegen.pojo.vo.PageResultVO;
import com.qiangesoft.bootcodegen.service.IBcgDataSourceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 数据源 服务实现类
 * </p>
 *
 * @author qiangesoft
 * @date 2024-03-13
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class BcgDataSourceServiceImpl extends ServiceImpl<BcgDataSourceMapper, BcgDataSource> implements IBcgDataSourceService {

    private final DataSource dataSource;

    private final DynamicDataSourceProperties properties;

    private final DataSourceCreator druidDataSourceCreator;

    private final DbMapper dbMapper;

    @Override
    public void initDataSource() {
        List<BcgDataSource> dataSourceList = this.list();
        if (CollectionUtils.isEmpty(dataSourceList)) {
            return;
        }
        DataSourceProperty mainDataSourceProperty = properties.getDatasource().get(properties.getPrimary());
        for (BcgDataSource dataSource : dataSourceList) {
            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            BeanUtils.copyProperties(mainDataSourceProperty, dataSourceProperty);
            dataSourceProperty.setPoolName(String.valueOf(dataSource.getId()));
            dataSourceProperty.setDriverClassName(dataSource.getDriverClassName());
            dataSourceProperty.setUrl(dataSource.getUrl());
            dataSourceProperty.setUsername(dataSource.getUsername());
            dataSourceProperty.setPassword(dataSource.getPassword());
            dataSourceProperty.setDruid(mainDataSourceProperty.getDruid());
            this.addDataSource(dataSourceProperty);
        }
    }

    @Override
    public void saveDataSource(BcgDataSourceDTO dataSourceDTO) {
        BcgDataSource dataSource = new BcgDataSource();
        dataSource.setName(dataSourceDTO.getName());
        dataSource.setDbType(dataSourceDTO.getDbType());
        dataSource.setDriverClassName(DbType.getDriverClassByName(dataSourceDTO.getDbType()));
        dataSource.setUrl(dataSourceDTO.getUrl());
        dataSource.setUsername(dataSourceDTO.getUsername());
        dataSource.setPassword(dataSourceDTO.getPassword());
        dataSource.setRemarks(dataSourceDTO.getRemarks());
        dataSource.setDeleted(false);
        baseMapper.insert(dataSource);
    }

    @Override
    public void removeDataSource(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);
    }

    @Override
    public void updateDataSource(Long id, BcgDataSourceDTO dataSourceDTO) {
        LambdaUpdateWrapper<BcgDataSource> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(BcgDataSource::getId, id)
                .set(BcgDataSource::getName, dataSourceDTO.getName())
                .set(BcgDataSource::getDbType, dataSourceDTO.getDbType())
                .set(BcgDataSource::getDriverClassName, DbType.getDriverClassByName(dataSourceDTO.getDbType()))
                .set(BcgDataSource::getUrl, dataSourceDTO.getUrl())
                .set(BcgDataSource::getUsername, dataSourceDTO.getUsername())
                .set(BcgDataSource::getPassword, dataSourceDTO.getPassword())
                .set(BcgDataSource::getRemarks, dataSourceDTO.getRemarks());
        baseMapper.update(null, updateWrapper);
    }

    @Override
    public PageResultVO<BcgDataSource> listDataSource(PageQuery pageQuery, BcgDataSourceQuery dataSourceQuery) {
        long pageNum = pageQuery.getPageNum();
        long pageSize = pageQuery.getPageSize();

        PageResultVO<BcgDataSource> pageResult = new PageResultVO<>();
        pageResult.setPageNum(pageNum);
        pageResult.setPageSize(pageSize);

        LambdaQueryWrapper<BcgDataSource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(dataSourceQuery.getDbType()), BcgDataSource::getDbType, (dataSourceQuery.getDbType()))
                .like(StringUtils.isNotBlank(dataSourceQuery.getName()), BcgDataSource::getName, dataSourceQuery.getName())
                .orderByDesc(BcgDataSource::getCreateTime);
        Page<BcgDataSource> page = baseMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
        pageResult.setTotal(page.getTotal());
        pageResult.setPages(page.getPages());
        pageResult.setResults(page.getRecords());

        return pageResult;
    }

    @Override
    public List<BcgDataSource> listAllDataSource(BcgDataSourceQuery dataSourceQuery) {
        LambdaQueryWrapper<BcgDataSource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(dataSourceQuery.getDbType()), BcgDataSource::getDbType, (dataSourceQuery.getDbType()))
                .eq(StringUtils.isNotBlank(dataSourceQuery.getName()), BcgDataSource::getName, dataSourceQuery.getName())
                .orderByDesc(BcgDataSource::getCreateTime);
        List<BcgDataSource> dataSourceList = baseMapper.selectList(queryWrapper);
        BcgDataSource bcgDataSource = new BcgDataSource();
        bcgDataSource.setId(CodegenConstant.MASTER_DATA_SOURCE_ID);
        bcgDataSource.setName(CodegenConstant.MASTER_DATA_SOURCE_NAME);
        bcgDataSource.setDbType(DbType.MySQL.name());
        DataSourceProperty mainDataSourceProperty = properties.getDatasource().get(properties.getPrimary());
        bcgDataSource.setDriverClassName(mainDataSourceProperty.getDriverClassName());
        bcgDataSource.setUrl(mainDataSourceProperty.getUrl());
        bcgDataSource.setUsername(mainDataSourceProperty.getUsername());
        bcgDataSource.setPassword(mainDataSourceProperty.getPassword());
        bcgDataSource.setDeleted(false);
        dataSourceList.add(0, bcgDataSource);
        return dataSourceList;
    }

    @Override
    public PageResultVO<BcgTableBO> listTable(PageQuery pageQuery, Long dataSourceId) {
        long pageNum = pageQuery.getPageNum();
        long pageSize = pageQuery.getPageSize();

        PageResultVO<BcgTableBO> pageResult = new PageResultVO<>();
        pageResult.setPageNum(pageNum);
        pageResult.setPageSize(pageSize);

        Page<BcgTableBO> page;
        try {
            // 不传dataSourceId则在默认数据源查找
            String dbType = DbType.MySQL.name();
            if (dataSourceId != null && !CodegenConstant.MASTER_DATA_SOURCE_ID.equals(dataSourceId)) {
                BcgDataSource bcgDataSource = baseMapper.selectById(dataSourceId);
                if (bcgDataSource != null) {
                    DynamicDataSourceContextHolder.push(String.valueOf(dataSourceId));
                    dbType = bcgDataSource.getDbType();
                }
            }
            page = dbMapper.listTable(new Page<>(pageNum, pageSize), dbType);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("数据源切换异常");
        } finally {
            DynamicDataSourceContextHolder.poll();
        }

        pageResult.setTotal(page.getTotal());
        pageResult.setPages(page.getPages());
        pageResult.setResults(page.getRecords());

        return pageResult;
    }

    @Override
    public List<BcgTableColumnBO> listTableColumn(Long dataSourceId, String tableName) {
        try {
            // 不传dataSourceId则在默认数据源查找
            String dbType = DbType.MySQL.name();
            if (dataSourceId != null && !CodegenConstant.MASTER_DATA_SOURCE_ID.equals(dataSourceId)) {
                BcgDataSource bcgDataSource = baseMapper.selectById(dataSourceId);
                if (bcgDataSource != null) {
                    DynamicDataSourceContextHolder.push(String.valueOf(dataSourceId));
                    dbType = bcgDataSource.getDbType();
                }
            }
            return dbMapper.listTableColumn(dbType, Collections.singletonList(tableName));
        } catch (Exception e) {
            throw new RuntimeException("数据源切换异常");
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

    @Override
    public void connection(Long id) {
        BcgDataSource dataSource = baseMapper.selectById(id);
        if (dataSource == null) {
            return;
        }
        DataSourceProperty mainDataSourceProperty = properties.getDatasource().get(properties.getPrimary());
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(mainDataSourceProperty, dataSourceProperty);
        dataSourceProperty.setPoolName(String.valueOf(dataSource.getId()));
        dataSourceProperty.setDriverClassName(dataSource.getDriverClassName());
        dataSourceProperty.setUrl(dataSource.getUrl());
        dataSourceProperty.setUsername(dataSource.getUsername());
        dataSourceProperty.setPassword(dataSource.getPassword());
        dataSourceProperty.setDruid(mainDataSourceProperty.getDruid());

        String poolName = dataSourceProperty.getPoolName();
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) this.dataSource;
        Map<String, DataSource> dataSources = dynamicRoutingDataSource.getDataSources();
        if (!dataSources.containsKey(poolName)) {
            DataSource newDataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);
            dynamicRoutingDataSource.addDataSource(poolName, newDataSource);
        }
    }

    /**
     * 添加数据源
     *
     * @param dataSourceProperty
     * @return
     */
    private void addDataSource(DataSourceProperty dataSourceProperty) {
        String poolName = dataSourceProperty.getPoolName();
        try {
            DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
            Map<String, DataSource> dataSources = dynamicRoutingDataSource.getDataSources();
            if (!dataSources.containsKey(poolName)) {
                DataSource newDataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);
                dynamicRoutingDataSource.addDataSource(poolName, newDataSource);
            }
        } catch (Exception e) {
            log.error("数据源[{}]初始化失败", poolName);
        }
    }
}
